﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;

namespace TimberSmart.Desktop
{
    // Attach it to a BindingSource to find adds, deletes and changes.
    public class BindingSourceTracker<T> : IDisposable where T: class
    {
        private BindingSource _bs;

        private T[] _initial;
        private bool[] _dirty;

        public BindingSourceTracker(BindingSource bs)
        {
            _bs = bs;
            _bs.ListChanged += _bs_ListChanged;
            _bs.DataSourceChanged += _bs_DataSourceChanged;

            if (_bs.DataSource is IEnumerable<T>)
            {
                _initial = ((IEnumerable<T>)_bs.DataSource).ToArray();
                _dirty = new bool[_initial.Count()];
            }
        }

        void _bs_DataSourceChanged(object sender, EventArgs e)
        {
            if (_bs.DataSource is IEnumerable<T>)
            {
                _initial = null;
                _dirty = null;

                _initial = ((IEnumerable<T>)_bs.DataSource).ToArray();
                _dirty = new bool[_initial.Count()];
            }
        }

        void _bs_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
        {
            if (!isValid()) return;

            if (e.ListChangedType == ListChangedType.ItemChanged)
            {
                T item = (T)_bs.Current;
                for (int i = 0; i < _initial.Length; i++)
                {
                    if (_initial[i] == item) _dirty[i] = true;
                }
            }
        }

        private bool isValid()
        {
            if (_bs.DataSource == null) return false;
            if (!(_bs.DataSource is IEnumerable<T>)) return false;
            return true;
        }

        public IEnumerable<T> GetAdded()
        {
            if (!isValid()) return null;

            var items = (IEnumerable<T>)_bs.DataSource;
            return items.Where(item => !(_initial.Contains(item)));
        }

        public IEnumerable<T> GetDeleted()
        {
            if (!isValid()) return null;

            var items = (IEnumerable<T>)_bs.DataSource;
            return _initial.Where(item => !items.Contains(item)).ToArray();
        }

        public IEnumerable<T> GetUpdated()
        {
            if (!isValid()) return null;            
            var items = (IEnumerable<T>)_bs.DataSource;
            return _initial.Where((item, index) => _dirty[index] && items.Contains(item)).ToArray();
        }

        public void Dispose()
        {
            _bs.ListChanged -= _bs_ListChanged;
            _bs.DataSourceChanged -= _bs_DataSourceChanged;
        }
    }
}
